﻿Imports Lucene.Net.Index
Imports System.IO
Imports DotNetNuke.Entities.Portals
Imports Lucene.Net.Documents

Namespace Aricie.DNN.Modules.LuceneSearch.Business.StoringIndexActions

    Public Class BackupIndexAction
        Implements [Interface].IStoringIndexAction

#Region "Accès paramètrez"
        Private _PDB As PortalDriverBase = Nothing

        Private ReadOnly Property LucenePortalSettings As Settings.LucenePortalSettings
            Get
                Return _PDB.LucenePortalSettings
            End Get
        End Property
#End Region

        Public Sub BeforeStorage(PDB As PortalDriverBase) Implements [Interface].IStoringIndexAction.BeforeStorage
            _PDB = PDB
            Dim nbBackupsUpdated As Integer = Me.UpdateBackups()

            If LucenePortalSettings.LogIndexingProcess Then

                BusinessController.LogIndexingStep(LuceneLogType.IndexProcessing, _PDB.PortalId, "Updated Backups", _
                                                    False, "Nb Backup folders updates", nbBackupsUpdated)

            End If
        End Sub

        Public Sub AfterStorage(PDB As PortalDriverBase) Implements [Interface].IStoringIndexAction.AfterStorage
            _PDB = PDB
            'Si le nouvel index ne convient pas, reprendre le backup
            Dim freshBackUp As String = GetMostRecentBackup()
            If Not IsNewIndexFaithFull(LucenePortalSettings.GetIndexFolderMapPath(_PDB.PortalId), freshBackUp) Then
                RollBackBackup()
                If LucenePortalSettings.LogIndexingProcess Then

                    BusinessController.LogIndexingStep(LuceneLogType.IndexProcessing, _PDB.PortalId, _
                                                        "Index discarded, Finished performing rollback", False)

                End If
            End If
        End Sub


        Private Function UpdateBackups() As Integer

            Dim toReturn As Integer = 0


            For Each backupFrequency As TimeSpan In LucenePortalSettings.BackupFrequencies.Keys


                Dim nbBackups As Integer = LucenePortalSettings.BackupFrequencies(backupFrequency)

                If CleanAndAssertNewBackups(LucenePortalSettings.GetBackupsRootFolderMapPath(_PDB.PortalId), backupFrequency, nbBackups) Then

                    CopyIndexToOtherFolder(LucenePortalSettings.GetIndexFolderMapPath(_PDB.PortalId), LucenePortalSettings.GetNewBackupMapPath(backupFrequency))
                    toReturn += 1
                End If

            Next

            Return toReturn
        End Function



        Private Sub RollBackBackup()


            Dim backUpToRollBack As String = GetMostRecentBackup()

            If backUpToRollBack <> "" Then
                CopyIndexToOtherFolder(backUpToRollBack, LucenePortalSettings.GetIndexFolderMapPath(_PDB.PortalId))
            End If


        End Sub


        Private Sub CopyIndexToOtherFolder(ByVal originPath As String, ByVal destinationPath As String)

            Dim origDir As Lucene.Net.Store.Directory = _PDB.GetDirectory(originPath)
            Dim destinationDir As Lucene.Net.Store.Directory = _PDB.GetDirectory(destinationPath)
            If IndexReader.IndexExists(origDir) Then

                Dim backupReader As IndexReader = IndexReader.Open(origDir, True)

                Dim backUpWriter As New IndexWriter(destinationDir, _PDB.GetStandardAnalyzer(), True, IndexWriter.MaxFieldLength.UNLIMITED)
                backUpWriter.AddIndexes(New IndexReader() {backupReader})

                backUpWriter.Dispose()
                backupReader.Dispose()
            End If

        End Sub

        Private Function IsNewIndexFaithFull(ByVal pathNewIndex As String, ByVal pathOldIndex As String) As Boolean

            Dim toReturn As Boolean = True

            If pathNewIndex <> "" AndAlso pathOldIndex <> "" Then

                Dim countNew As Integer = 0
                Dim countOld As Integer = 0
                Try
                    Dim newIndexReader As IndexReader = IndexReader.Open(_PDB.GetDirectory(pathNewIndex), True)
                    countNew = newIndexReader.NumDocs
                    newIndexReader.Dispose()
                Catch ex As Exception
                    'le nombre d'element reste a 0, on a pas pu ouvrir l'index
                    Aricie.Services.ExceptionHelper.LogException(ex)
                End Try

                Try
                    Dim backedUpIndexReader As IndexReader = IndexReader.Open(_PDB.GetDirectory(pathOldIndex), True)
                    countOld = backedUpIndexReader.NumDocs
                    backedUpIndexReader.Dispose()
                Catch ex As Exception
                    'le nombre d'element reste a 0, on a pas pu ouvrir l'index
                    Aricie.Services.ExceptionHelper.LogException(ex)
                End Try


                If countOld > 0 Then

                    Dim percentDelta As Double = Math.Abs(100 * (countNew - countOld) / countOld)

                    If percentDelta > LucenePortalSettings.AlertDocChangeRate Then

                        Dim _
                            body As String = "Lors d'une mise à jour, l'index a changé de  " & percentDelta.ToString() & _
                                             "pourcents"

                        If percentDelta > LucenePortalSettings.RejectDocChangeRate Then
                            toReturn = False
                            body = "Lors d'une mise à jour, l'index aurait changé  de " & percentDelta.ToString() & _
                                   "pourcents. la mise à jour a donc été annulée"
                        End If

                        SendAlertEmail(body)

                    End If

                End If

            End If

            Return toReturn

        End Function

        Protected Friend Sub SendAlertEmail(ByVal body As String)

            Dim emailToAddress As String = LucenePortalSettings.AlertEmail
            Dim pInfo As PortalInfo = New PortalController().GetPortal(_PDB.PortalId)
            Dim emailFrom As String = pInfo.Email
            Dim subject As String = "Alerte Lucene/DNN sur portail " & pInfo.PortalName
            'Les paramètres suivant du mail sont vides, comme dans la méthode mail.SendMail(ByVal user As UserInfo, ByVal msgType As MessageType, ByVal settings As PortalSettings)
            Dim bcc As String = ""
            Dim attachement As String = ""
            Dim bodytype As String = DotNetNuke.Services.Mail.MailFormat.Html.ToString
            Dim smtpserver As String = ""
            Dim smtpauthentication As String = ""
            Dim smtppassword As String = ""
            Dim smtpUser As String = ""

            DotNetNuke.Services.Mail.Mail.SendMail(emailFrom, emailToAddress, bcc, subject, body, attachement, bodytype, smtpserver, _
                           smtpauthentication, smtpUser, smtppassword)

        End Sub


        Private Function CleanAndAssertNewBackups(ByVal folderMapPath As String, ByVal backupPeriod As TimeSpan, _
                                                   ByVal nbToKeep As Integer) As Boolean
            Dim toreturn As Boolean = True
            Dim deleteLimitAsLong As Long = DateTools.StringToTime(DateTools.DateToString(Now.Subtract(TimeSpan.FromHours(nbToKeep * backupPeriod.TotalHours)), DateTools.Resolution.HOUR))
            Dim newLimitDateAsLong As Long = DateTools.StringToTime(DateTools.DateToString(Now.Subtract(backupPeriod), DateTools.Resolution.HOUR))


            Dim existingBackups As String() = GetBackupFileNames(folderMapPath, backupPeriod)
            For Each backup As String In existingBackups
                Dim backupDir As Lucene.Net.Store.Directory = _PDB.GetDirectory(backup)
                If IndexReader.IndexExists(backupDir) Then
                    Dim indexLastModif As Long = IndexReader.LastModified(backupDir)
                    If indexLastModif < deleteLimitAsLong Then
                        Directory.Delete(backup, True)
                    ElseIf indexLastModif >= newLimitDateAsLong Then
                        toreturn = False
                    End If
                End If
            Next
            Return toreturn
        End Function

        Private Function GetBackupFileNames(ByVal baseFoldermapPath As String, ByVal backupPeriod As TimeSpan) _
            As String()

            Return _
                Directory.GetFileSystemEntries(baseFoldermapPath, _
                                                Settings.LucenePortalSettings.GetCommonBackupRelativePath(backupPeriod) & "*")
        End Function


        Private Function GetMostRecentBackup() As String

            Dim backupPaths As New List(Of String)
            For Each backupFrequency As TimeSpan In LucenePortalSettings.BackupFrequencies.Keys

                backupPaths.AddRange( _
                                      GetBackupFileNames(LucenePortalSettings.GetBackupsRootFolderMapPath(_PDB.PortalId), _
                                                          backupFrequency))

            Next
            Return GetMostRecentBackup(backupPaths)
        End Function

        Private Function GetMostRecentBackup(ByVal backupPaths As IEnumerable(Of String)) As String
            Dim toReturn As String = ""
            Dim bestDateSoFar As Long = Long.MinValue
            For Each backup As String In backupPaths
                Dim backupDir As Lucene.Net.Store.Directory = _PDB.GetDirectory(backup)
                If IndexReader.IndexExists(backupDir) Then
                    Dim indexLastModif As Long = IndexReader.LastModified(backupDir)
                    If indexLastModif > bestDateSoFar Then
                        toReturn = backup
                        bestDateSoFar = indexLastModif
                    End If
                End If
            Next
            Return toReturn
        End Function



    End Class
End Namespace